package com.example.framework509.common.services.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.framework509.common.annotation.Remind;
import com.example.framework509.common.annotation.entityAnnotations.VerifyMultiRepetitionField;
import com.example.framework509.common.annotation.entityAnnotations.VerifyRepetitionField;
import com.example.framework509.common.entities.ResMsg;
import com.example.framework509.common.exceptions.*;
import com.example.framework509.common.services.EntityBaseService;
import com.example.framework509.common.services.Impl.base.BaseReflectionServiceImpl;
import com.example.framework509.common.utils.RedisUtil;
import com.mysql.cj.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.NonTransientDataAccessException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.*;

public abstract class AutoEntityServiceImpl<E> extends BaseReflectionServiceImpl<E> implements EntityBaseService<E> {

    @Autowired
    protected RedisUtil redisUtil;
    @Autowired
    public BaseMapper<E> mapper;

    protected final Class<?> ENTITY_CLASS = getEntityClass();
    protected final String ENTITY_NAME = ENTITY_CLASS.getSimpleName();
    protected final String ENTITY_NAME_LOWER = toLower(ENTITY_NAME);
    @Remind("由数据库实体中的 @RequiredField 注解标识，标识新增、修改项")
    protected final List<String> REQUIRED_FIELD = getRequiredFields();
    @Remind("由数据库实体中的 @VerifyRepetitionField 注解标识，标识用于单一验重的字段")
    protected final List<String> VERIFY_REPETITION_FIELD = getVerifyRepetitionFields();
    @Remind("由数据库实体中的 @VerifyRepetitionField 注解标识，标识用于组合验重的字段")
    protected final List<String> VERIFY_MULTI_REPETITION_FIELD = getVerifyMultiRepetitionFields();
    //注意：需要插入或更新的字段都要加@RequiredField注解，且数据库实体与传参实体之间的字段名要完全对应

    @Override
    public E getEntityFromId(Integer entityId, boolean allowNull) {
        return getEntityFromRedisProp("id", entityId.toString(), allowNull);
    }

    @Override
    public E getEntityFromId(Integer entityId) {
        return getEntityFromId(entityId, true);
    }

    @Override
    public E getEntityFromRedisProp(String propName, String value, boolean allowNull) {
        getAllEntityNum();
        NotExistException notExistException = new NotExistException(propName + "为" + value + "的" + ENTITY_NAME + "不存在");
        if (!VERIFY_REPETITION_FIELD.contains(propName) && !propName.equals("id") && !propName.equals("md5"))
            throw new AnnotationException("该属性名不是唯一字段，也不是md5");
        if (propName.equals("md5") && VERIFY_MULTI_REPETITION_FIELD.size() == 0)
            throw new AnnotationException("查询属性名为md5，但业务实体没有组合唯一字段");
        String propNameLower = toLower(propName);
        String propNameUpper = toUpper(propName);
        if (redisUtil.hasKey(ENTITY_NAME_LOWER + propNameUpper + ":" + value)) {
            Object valueInRedis = redisUtil.get(ENTITY_NAME_LOWER + propNameUpper + ":" + value);
            if (valueInRedis != null && !propName.equals("id"))
                return getEntityFromId(Integer.parseInt(valueInRedis.toString()), allowNull);
            else if (valueInRedis != null) return (E) valueInRedis;
            else {
                if (allowNull) return null;
                else throw notExistException;
            }
        } else {
            //针对不含"md5"字段的业务实体，只能通过 redis 获取，因此重刷该业务实体的 redis
            if (propName.equals("md5")) {
                getAllEntitiesToRedis();
                Object md5EntityId = redisUtil.get(ENTITY_NAME_LOWER + "Md5:" + value);
                if (md5EntityId == null) {
                    if (allowNull) return null;
                    else throw notExistException;
                }
                Integer entityId = Integer.parseInt(md5EntityId.toString());
                return getEntityFromId(entityId, allowNull);
            }
            QueryWrapper<E> wrapper = new QueryWrapper<>();
            wrapper.eq(underscoreName(propNameLower), value);
            E entity = mapper.selectOne(wrapper);
            if (entity != null) addEntityToRedis(entity);
            else {
                //若数据库中也没有，则在 redis 添加此键，值为 null
                redisUtil.set(ENTITY_NAME_LOWER + propNameUpper + ":" + value, null);
                if (!allowNull) throw notExistException;
            }
            return entity;
        }
    }

    @Override
    public E getEntityFromRedisProp(String propName, String value) {
        return getEntityFromRedisProp(propName, value, true);
    }

    public E getEntityByMultiVerify(List<Object> verifyFields) {
        String md5 = getMd5(verifyFields);
        return getEntityFromRedisProp("md5", md5, false);
    }

    @Override
    public E getEntityFromParam(Object param, boolean byId, boolean allowNull) {
        Field[] paramFields = param.getClass().getDeclaredFields();
        //优先找id
        if (byId) {
            for (Field paramField : paramFields) {
                int id = 0;
                if (paramField.getName().equals("id")) try {
                    Object idField = getFieldValue(param, "id");
                    if (idField == null) break;
                    id = Integer.parseInt(idField.toString());
                } catch (Exception e) {
                    throw new ParamFieldException("参数取值错误@Integer.parseInt");
                }
                if (paramField.getName().equals("id")) return getEntityFromId(id, allowNull);
            }
        }
        //其次找唯一字段
        for (String fieldName : VERIFY_REPETITION_FIELD) {
            for (Field paramField : paramFields) {
                if (paramField.getName().equals(fieldName)) {
                    Object fieldValue = getFieldValue(param, fieldName);
                    if (fieldValue == null) break;
                    E entity = getEntityFromRedisProp(fieldName, fieldValue.toString(), allowNull);
                    if (entity != null) return entity;
                }
            }
        }
        //最后找组合唯一字段
        if (VERIFY_MULTI_REPETITION_FIELD.size() > 0) {
            List<Object> fieldsValues = new ArrayList<>();
            for (String fieldName : VERIFY_MULTI_REPETITION_FIELD) {
                fieldsValues.add(getFieldValue(param, fieldName));
            }
            String md5 = getMd5(fieldsValues);
            return getEntityFromRedisProp("md5", md5, allowNull);
        }
        //不是id、唯一字段或组合唯一字段则
        if (allowNull) return null;
        else throw new NotExistException();
    }

    @Override
    public E getEntityFromParam(Object param, boolean byId) {
        return getEntityFromParam(param, byId, true);
    }

    @Override
    public Integer getAllEntityNum() {
        Integer numInSQL = mapper.selectCount(null);
        Integer numInRedis = sGetValuesFromRedisKey(ENTITY_NAME_LOWER + "Ids").size();
        if (numInRedis.equals(numInSQL)) return numInRedis;
        else return getAllEntitiesToRedis().size();
    }

    @Override
    public List<E> getAllEntities() {
        return getEntitiesByPage(0, Integer.MAX_VALUE);
    }

    @Override
    public List<E> getEntitiesByPropName(String propName, Object value) {
        Map<String, Object> map = new HashMap<>();
        map.put(propName, value);
        return getEntitiesByMap(map, false);
    }

    @Override
    public List<E> getEntitiesByPage(Integer page, Integer pageSize) {
        return getEntitiesByPage(page, pageSize, false);
    }

    @Override
    public List<E> getEntitiesByMap(Map searchMap, boolean reverse) {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (Object key : searchMap.keySet()) {
            if (key.equals("page") || key.equals("size") || searchMap.get(key).toString().equals("")) continue;
            try {
                if (key.toString().contains("BeginDate")) {
                    queryWrapper.ge(underscoreName(key.toString().replace("BeginDate", "")), new Date(sdf.parse(searchMap.get(key).toString()).getTime()));
                    continue;
                } else if (key.toString().contains("EndDate")) {
                    queryWrapper.le(underscoreName(key.toString().replace("EndDate", "")), new Date(sdf.parse(searchMap.get(key).toString()).getTime() + 86400000));
                    continue;
                }
            } catch (Exception e) {
                throw new ParamFieldException("日期格式出错，需要：yyyy-MM-dd");
            }
            try {
                if (key.toString().contains("Min")) {
                    queryWrapper.ge(underscoreName(key.toString().replace("Min", "")), searchMap.get(key).toString());
                    continue;
                } else if (key.toString().contains("Max")) {
                    queryWrapper.le(underscoreName(key.toString().replace("Max", "")), searchMap.get(key).toString());
                    continue;
                }
            } catch (Exception e) {
                throw new ParamFieldException("Min/Max标识的字段不可比较");
            }
            queryWrapper.like(underscoreName(key.toString()), searchMap.get(key));
        }
        try {
            queryWrapper.orderBy(true, !reverse, "id");
            return mapper.selectList(queryWrapper);
        } catch (Exception e) {
            throw new ParamFieldException("参数中某字段不在该表中");
        }
    }

    @Override
    public List<E> sGetEntitiesFromRedisKey(String redisKey) {
        List<Object> values = sGetValuesFromRedisKey(redisKey);
        List<E> entities = new ArrayList<>();
        if (redisKey.equals(ENTITY_NAME_LOWER + "Id")) {
            values.forEach(value -> entities.add((E) value));
        } else {
            values.forEach(value -> entities.add(getEntityFromId(Integer.parseInt(value.toString()))));
        }
        return entities;
    }

    @Override
    public List<Object> sGetValuesFromRedisKey(String redisKey) {
        Set<Object> valueSet = redisUtil.sGet(redisKey);
        if (valueSet == null || valueSet.size() == 0) {
            getAllEntitiesToRedis();
            valueSet = redisUtil.sGet(redisKey);
        }
        return new ArrayList<>(valueSet);
    }

    @Override
    public ResMsg baseQueryNum() {
        Map<String, Object> map = new HashMap<>();
        map.put("totalCount", getAllEntityNum());
        return ResMsg.successRes(map);
    }

    @Remind("添加的是且仅是参数实体的 @RequiredField 字段" +
            "使用数据库实体做参数实体，注意 所有非数据库定义字段都要加 @RequiredField ,设定好 @AllowNull 控制传空")
    @Override
    public ResMsg baseQueryAdd(Object paramEntity) {
        getAllEntityNum();
        //判断是否重名
        E repeatEntity = verifyEntityRepetition(paramEntity);
        if (repeatEntity != null) {
            Map<String, Object> returnMap = new HashMap<>();
            returnMap.put(repeatEntity.getClass().getSimpleName(), repeatEntity);
            return ResMsg.failRes("有重复项", returnMap);
        }
        //从参数创建实体对象
//        E jdbcEntity = createEntityFromParamObject(paramEntity);
        E jdbcEntity = (E)paramEntity;
        //插入数据库
        Integer newId = insertEntity(jdbcEntity);
        Map<String, Object> returnMap = new HashMap<>();
        returnMap.put("newId", newId);
        return ResMsg.successRes(returnMap);
    }

//    public ResMsg baseQueryAddWithCustomId(Object paramEntity) {
//        Object idObj = getFieldValue(paramEntity, TABLE_ID_FIELD.get(0));
//        int id = Integer.parseInt(idObj.toString());
//        E entityFromId = getEntityFromId(id, true);
//        if (entityFromId != null)
//            throw new JdbcException("ID 已存在");
//        //判断是否重名
//        verifyEntityRepetition(paramEntity);
//        //从参数创建实体对象
//        E jdbcEntity = createEntityFromParamObjectWithId(paramEntity);
//        //插入数据库
//        insertEntity(jdbcEntity);
//        return ResMsg.successRes(null);
//    }

    @Override
    public ResMsg baseQueryDelOneById(Integer entityId) {
        return baseQueryDelOneByRedisProp("id", entityId.toString());
    }

    @Override
    public ResMsg baseQueryDelOneByRedisProp(String propName, String value) {
        E delEntity = getEntityFromRedisProp(propName, value, false);
        deleteEntity(delEntity);
        return ResMsg.successRes(null);
    }

    @Override
    public ResMsg baseQueryDelOneByParam(Object param) {
        E oneEntityFromParam = getEntityFromParam(param, true, false);
        return baseQueryDelOneById(Integer.parseInt(getFieldValue(oneEntityFromParam, "id").toString()));
    }

    @Override
    public ResMsg baseQueryEditById(Object paramEntity) {
        return baseQueryEditByParam(paramEntity, true);
    }

    @Override
    public ResMsg baseQueryEditByParam(Object paramEntity, boolean byId) {
        E entityFromParam = getEntityFromParam(paramEntity, byId, false);
        Integer entityId = Integer.parseInt(getFieldValue(entityFromParam, "id").toString());
        E oldEntity = getEntityFromId(entityId, false);
        E updateEntity = getEntityFromId(entityId);
        //更新字段值
        if (REQUIRED_FIELD.size() == 0) throw new AnnotationException("实体无可更改的字段");
        for (String fieldName : REQUIRED_FIELD) {
            Object value = getFieldValue(paramEntity, fieldName);
            setFieldValue(updateEntity, fieldName, value);
        }
        if (getFields(paramEntity.getClass()).contains("createTime")) setFieldValue(updateEntity, "createTime", null);
        if (getFields(paramEntity.getClass()).contains("editTime")) setFieldValue(updateEntity, "editTime", null);
        //对更新后的实体对象验重
        E repeatEntity = verifyEntityRepetition(paramEntity, entityId);
        if (repeatEntity != null) {
            Map<String, Object> returnMap = new HashMap<>();
            returnMap.put(repeatEntity.getClass().getSimpleName(), repeatEntity);
            return ResMsg.failRes("有重复项", returnMap);
        }
        //验重通过，更新数据库
        updateEntity(updateEntity, oldEntity);
        return ResMsg.successRes(null);
    }

    @Remind("为了简化及避免错误，模糊查询请求里的参数名写为下划线字母代表大写，比如 user_id ")
    //如果返回成功但是data为null，那么不需要更新表单，代表没有查询条件
    //如果返回成功data为空数组，那么为查询为空
    @Override
    public ResMsg baseQuerySearch(Object searchParamEntity) {
        Map<String, Object> searchMap = new HashMap<>();
        Field[] declaredFields = searchParamEntity.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            Object value = getFieldValue(searchParamEntity, field.getName());
            if (value == null || StringUtils.isNullOrEmpty(value.toString())) continue;
            searchMap.put(field.getName(), value);
        }
        if (searchMap.size() == 0) return ResMsg.successRes(null);
        List<E> res = mapper.selectByMap(searchMap);
        Map<String, Object> map = new HashMap<>();
        map.put("searchResult", res);
        return ResMsg.successRes(map);
    }

    @Remind("查询条件按需传入Map，注意：必须包含page和size键")
    @Override
    public ResMsg baseQuerySearchByMap(Map searchMap, boolean reverse) {
        if (searchMap == null) searchMap = new HashMap<String, Object>();
        int page, size;
        if (searchMap.containsKey("page") && searchMap.containsKey("size")) {
            try {
                page = Integer.parseInt(searchMap.get("page").toString());
                size = Integer.parseInt(searchMap.get("size").toString());
            } catch (Exception e) {
                throw new ParamFieldException("page或size值转换integer失败");
            }
        } else {
            page = 0;
            size = Integer.MAX_VALUE;
        }
        List<E> result;
        //判断有无条件
        boolean hasCondition = false;
        for (Object key : searchMap.keySet()) {
            if (key.equals("page") || key.equals("size")) {
            } else if (!searchMap.get(key).equals("")) {
                hasCondition = true;
                break;
            }
        }
        Integer num = 0;
        //若没条件直接走redis
        if (!hasCondition) {
            if (reverse) {
                result = (List<E>) baseQueryPageDataReverse(page, size).getData().get("data");
            } else {
                result = (List<E>) baseQueryPageData(page, size).getData().get("data");
            }
            num = getAllEntityNum();
        } else {
            List<E> entitiesByMap = getEntitiesByMap(searchMap, reverse);
            result = pageList(entitiesByMap, page, size);
            num = entitiesByMap.size();
        }
        //包装结果
        Map<String, Object> map = new HashMap<>();
        map.put("searchResult", result);
        map.put("searchResultNum", num);
        return ResMsg.successRes(map);
    }

    @Override
    public ResMsg baseQuerySearchByMap(Map searchMap) {
        return baseQuerySearchByMap(searchMap, false);
    }

    @Override
    public ResMsg baseQuerySearchByMapReverse(Map searchMap) {
        return baseQuerySearchByMap(searchMap, true);
    }

    @Override
    public ResMsg baseQueryPageData(Integer page, Integer pageSize) {
        Map<String, Object> map = new HashMap<>();
        map.put("data", getEntitiesByPage(page, pageSize));
        return ResMsg.successRes(map);
    }

    @Override
    public ResMsg baseQueryPageDataReverse(Integer page, Integer pageSize) {
        Map<String, Object> map = new HashMap<>();
        map.put("data", getEntitiesByPage(page, pageSize, true));
        return ResMsg.successRes(map);
    }

    /**
     * 将实体插入数据库和 Redis
     *
     * @param insertEntity -要插入的实体对象
     */
    public Integer insertEntity(E insertEntity) {
        try {
            int insertState = mapper.insert(insertEntity);
            if (insertState == 1) {
                addEntityToRedis(insertEntity);
                return Integer.parseInt(getFieldValue(insertEntity, "id").toString());
            } else return -1;
        } catch (Exception e) {
            if (e instanceof NonTransientDataAccessException) throw new JdbcException("数据库参数转换失败，请检查类型或长度");
            throw new JdbcException("数据库插入失败@public boolean insertEntity(E insertEntity)");
        }
    }

    /**
     * 将实体从数据库和 Redis删除
     *
     * @param deleteEntity -要删除的实体对象
     */
    public void deleteEntity(E deleteEntity) {
        Integer delId = (Integer) getFieldValue(deleteEntity, "id");
        int delState = mapper.deleteById(delId);
        if (delState == 1) {
            delEntityFromRedis(deleteEntity);
        } else throw new JdbcException("数据库删除失败@public boolean deleteEntity(E deleteEntity)");
    }

    /**
     * 将数据库和 Redis 中的实体更新，redis 更新的方式为先删除再添加
     *
     * @param updateEntity -要更新的实体对象
     * @param oldEntity    -包含旧数据的、要删除的旧实体对象
     */
    public void updateEntity(E updateEntity, E oldEntity) {
        try {
            Integer id = (Integer) getFieldValue(updateEntity, "id");
            int insertState = mapper.updateById(updateEntity);
            if (insertState == 1) {
                delEntityFromRedis(oldEntity);
                E entityAfterUpdate = mapper.selectById(id);
                addEntityToRedis(entityAfterUpdate);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new JdbcException("数据库更新失败 @public void updateEntity(E updateEntity, E oldEntity) + " + e.getMessage());
        }
    }

    /**
     * 将实体对象的基本信息添加至 redis，包含：
     * entityId->entity
     * entityIds->entityId(set)
     * entityOnlyField->entityId
     * entityMultiFieldMd5->entityId
     * 有的可能用不到，但为了统一暂时都加了
     *
     * @param entity-添加 redis 的信息来源
     */
    public void addEntityToRedis(E entity) {
        //id
        Integer entityId = (Integer) getFieldValue(entity, "id");
        redisUtil.set(ENTITY_NAME_LOWER + "Id:" + entityId, entity);
        redisUtil.sSet(ENTITY_NAME_LOWER + "Ids", entityId);
        //唯一字段
        if (VERIFY_REPETITION_FIELD.size() != 0) {
            for (String fieldName : VERIFY_REPETITION_FIELD) {
                String fieldNameUpper = toUpper(fieldName);
                redisUtil.set(ENTITY_NAME_LOWER + fieldNameUpper + ":" + getFieldValue(entity, fieldName), entityId);
            }
        }
        //组合唯一字段
        if (VERIFY_MULTI_REPETITION_FIELD.size() != 0) {
            List<Object> fieldsValues = new ArrayList<>();
            for (String fieldName : VERIFY_MULTI_REPETITION_FIELD) {
                fieldsValues.add(getFieldValue(entity, fieldName));
            }
            String md5 = getMd5(fieldsValues);
            redisUtil.set(ENTITY_NAME_LOWER + "Md5:" + md5, entityId);
        }
        //额外的需要实现的加redis方法
        try {
            addEntityOtherToRedis(entity);
        } catch (Exception e) {
            throw new RedisException("自定义加redis方法异常");
        }
    }

    /**
     * 对应添加 redis 的方法，将添加的键值进行相应删除
     *
     * @param entity-删除 redis 的信息来源
     */
    public void delEntityFromRedis(E entity) {
        //id
        Integer entityId = (Integer) getFieldValue(entity, "id");
        redisUtil.del(ENTITY_NAME_LOWER + "Id:" + entityId);
        redisUtil.setRemove(ENTITY_NAME_LOWER + "Ids", entityId);
        //唯一字段
        if (VERIFY_REPETITION_FIELD.size() != 0) {
            for (String fieldName : VERIFY_REPETITION_FIELD) {
                String fieldNameUpper = toUpper(fieldName);
                redisUtil.del(ENTITY_NAME_LOWER + fieldNameUpper + ":" + getFieldValue(entity, fieldName));
            }
        }
        //组合唯一字段
        if (VERIFY_MULTI_REPETITION_FIELD.size() != 0) {
            List<Object> fieldsValues = new ArrayList<>();
            for (String fieldName : VERIFY_MULTI_REPETITION_FIELD) {
                fieldsValues.add(getFieldValue(entity, fieldName));
            }
            String md5 = getMd5(fieldsValues);
            redisUtil.del(ENTITY_NAME_LOWER + "Md5:" + md5);
        }
        //额外的需要实现的删redis方法
        try {
            delEntityOtherFromRedis(entity);
        } catch (Exception e) {
            throw new RedisException("自定义删redis方法异常");
        }
    }

    /**
     * 除了id、唯一字段、组合唯一字段，其他想要加入 redis 的需要实现此方法
     *
     * @param entity-添加 redis 的信息来源
     */
    public abstract void addEntityOtherToRedis(E entity);

    /**
     * 需要实现与添加 redis 方法对应的删除操作
     *
     * @param entity-删除 redis 的信息来源
     */
    public abstract void delEntityOtherFromRedis(E entity);

    //region ########################################### 内部调用 ######################################

    /**
     * 从该业务实体类中获取加有某注解的字段名们
     *
     * @param annotationClass-注解类型.class
     * @param <T>-注解类型
     * @return 加有某注解的字段名 List
     */
    private <T extends Annotation> List<String> getFieldsWithAnnotation(Class<T> annotationClass) {
        List<String> annotationFields = new ArrayList<>();
        Field[] declaredFields = ENTITY_CLASS.getDeclaredFields();
        for (Field field : declaredFields) {
            T requiredFieldAnnotation = field.getDeclaredAnnotation(annotationClass);
            if (requiredFieldAnnotation != null) annotationFields.add(field.getName());
        }
        return annotationFields;
    }

    private List<String> getFields(Class<?> annotationClass) {
        List<String> fieldNames = new ArrayList<>();
        Field[] declaredFields = ENTITY_CLASS.getDeclaredFields();
        for (Field field : declaredFields) {
            fieldNames.add(field.getName());
        }
        return fieldNames;
    }

    /**
     * 获取请求参数必须包含的字段名们
     *
     * @return 请求参数必须包含的字段名 List
     */
    private List<String> getRequiredFields() {
        List<String> requiredFields = new ArrayList<>();
        Field[] declaredFields = ENTITY_CLASS.getDeclaredFields();
        for (Field field : declaredFields) {
            String name = field.getName();
            if (!(name.equals("id") || name.equals("isDeleted") || name.equals("version") ||
                    name.equals("creator") || name.equals("createTime") || name.equals("editor") || name.equals("editTime")))
                requiredFields.add(field.getName());
        }
        return requiredFields;
    }

    /**
     * 获取需要单一验重的字段名们
     *
     * @return 需要验证重名的字段名 List
     */
    private List<String> getVerifyRepetitionFields() {
        return getFieldsWithAnnotation(VerifyRepetitionField.class);
    }

    /**
     * 获取需要组合验重的字段名们
     *
     * @return 需要验证重名的字段名 List
     */
    private List<String> getVerifyMultiRepetitionFields() {
        return getFieldsWithAnnotation(VerifyMultiRepetitionField.class);
    }

    /**
     * 用于编辑时的验重，与新增验重的区别是：当查找到相同验重字段的实体，还需判断是不是自身，不是自身才算重名
     *
     * @param paramEntity-需要验重的参数实体
     * @param oldId-需要修改的实体         id
     */
    private E verifyEntityRepetition(Object paramEntity, Integer oldId) {
        E oneEntityFromParam = getEntityFromParam(paramEntity, false, true);
        if (oneEntityFromParam != null && !getFieldValue(oneEntityFromParam, "id").equals(oldId))
            return oneEntityFromParam;
        else return null;
    }

    /**
     * 用于新增时的验重
     *
     * @param paramEntity-需要验重的参数实体
     */
    private E verifyEntityRepetition(Object paramEntity) {
        return verifyEntityRepetition(paramEntity, null);
    }

    public E createEntityFromMap(Map<String, Object> parameterMap) {
        E jdbcEntity;
        try {
            //创建空对象
            jdbcEntity = (E) ENTITY_CLASS.getDeclaredConstructor().newInstance();
            //为对象赋值
            Field[] declaredFields = ENTITY_CLASS.getDeclaredFields();
            for (Field field : declaredFields) {
                if (parameterMap.containsKey(field.getName()))
                    setFieldValue(jdbcEntity, field.getName(), parameterMap.get(field.getName()));
            }
            return jdbcEntity;
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            e.printStackTrace();
            throw new ParamFieldException("参数实体和JDBC实体字段命名或类型不统一");
        }
    }

    /**
     * 根据参数实体创建一个实体对象
     *
     * @param paramEntity-用于创建实体对象的参数实体
     * @return 创建的实体对象
     */
    private E createEntityFromParamObject(Object paramEntity) {
        E jdbcEntity;
        try {
            //创建空对象
            jdbcEntity = (E) ENTITY_CLASS.getDeclaredConstructor().newInstance();
            //为对象赋值
            if (REQUIRED_FIELD.size() == 0) throw new AnnotationException("实体缺少@RequiredField注解");
            for (String fieldName : REQUIRED_FIELD) {
                Object value = getFieldValue(paramEntity, fieldName);
                setFieldValue(jdbcEntity, fieldName, value);
            }
            return jdbcEntity;
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            e.printStackTrace();
            throw new ParamFieldException("参数实体和JDBC实体字段命名或类型不统一");
        }
    }

    /**
     * 从数据库中拿到所有数据并且加入 Redis
     *
     * @return 所有实体对象的 List
     */
    private List<E> getAllEntitiesToRedis() {
        System.out.println("除非清了redis，否则不应该进到这里@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
        List<E> entities = new ArrayList<>();
        try {
            entities = mapper.selectList(null);
        } catch (Exception e) {
            throw new JdbcException("数据库查询失败，@getAllEntitiesToRedis()，+ " + e.getMessage()); //检查实体类有没有空构造方法
        }
        redisUtil.getKeysBeginWith(ENTITY_NAME_LOWER + "*").forEach(redisUtil::del);
        for (E entity : entities) {
            addEntityToRedis(entity);
        }
        return entities;
    }

    /**
     * 从 Redis 中获取分页数据 List ，若不存在，则从数据库获取所有实体并加入 Redis ，再从 Redis 分页查询
     *
     * @param page-页码
     * @param pageSize-每页条数
     * @param reverse-是否倒序，true为倒序
     * @return 含顺序的分页实体 List
     */
    private List<E> getEntitiesByPage(Integer page, Integer pageSize, boolean reverse) {
        if (page < 0 || pageSize < 1 || page * pageSize >= getAllEntityNum()) return new ArrayList<>();
        Set<Object> entityIds = redisUtil.sGet(ENTITY_NAME_LOWER + "Ids", page, pageSize, reverse);
        if (entityIds == null || entityIds.size() == 0) {
            getAllEntitiesToRedis();
            entityIds = redisUtil.sGet(ENTITY_NAME_LOWER + "Ids", page, pageSize, reverse);
        }
        List<E> entities = new ArrayList<>();
        for (Object entityId : entityIds) {
            entities.add((E) redisUtil.get(ENTITY_NAME_LOWER + "Id:" + entityId));
        }
        return entities;
    }

    //endregion

}
